#include "Application.h"

#include "Core/GlobalContext.h"

#include "Core/Timer.hpp"

#include "Events/MouseEvent.h"
#include "Events/WindowEvent.h"
#include "Events/KeyboardEvent.h"

namespace SE
{
    void Application::start()
    {
        SE_PROFILE_FUNCTION();

        LOG_INFO("Engine start!");

        GlobalContext::GetWindowContext()->eventCallback = { [this](Event& event) -> void { onEvent(event); } };
        mContext.running = true;
        mContext.minimized = false;
        mContext.handleWindowEvents = true;
        mContext.handleApplicationEvents = true;
        mContext.handleKeyboardEvents = true;
        mContext.handleMouseEvents = true;
    }

    void Application::run()
    {
        SE_PROFILE_FUNCTION();

        static U64 begin, end;
        static Float deltaMilliseconds, inv = 1.0f / 1000.0f;
        while (mContext.running)
        {
            begin = Timer::Now<Microseconds>();
            tickOneFrame(deltaMilliseconds);
            end = Timer::Now<Microseconds>();
            deltaMilliseconds = (end - begin) * inv;
        }
    }

    void Application::shutdown()
    {
        SE_PROFILE_FUNCTION();

        for (auto& layer : mContext.layers)
        {
            layer->onDetach();
            layer.reset();
        }
        
        LOG_INFO("Engine shutdown!");
    }

    void Application::pushLayer(const Ref<Layer>& layer)
    {
        SE_PROFILE_FUNCTION();

        mContext.layers.push_back(layer);
        layer->onAttach();
    }

    void Application::onEvent(Event& event)
    {
        SE_PROFILE_FUNCTION();

        EventDispatcher dispatcher(event);

        if (mContext.handleWindowEvents)
        {
            dispatcher.dispatch<WindowShouldCloseEvent>([this](Event& evt)
            {
                mContext.running = false;
                return true;
            });
            dispatcher.dispatch<WindowResizeEvent>([this](Event& evt)
            {
                return true;
            });
        }
        if (mContext.handleApplicationEvents)
        {
        }
        if (mContext.handleKeyboardEvents)
        {
        }
        if (mContext.handleMouseEvents)
        {
        }

        // Dispatch events to layers.
        for (auto& layer : mContext.layers)
        {
            if (event.handled)
                break;
            layer->onEvent(event);
        }
    }

    void Application::tickOneFrame(const Double deltaMilliseconds)
    {
        SE_PROFILE_FUNCTION();

        WindowAPI::PollEvents();

        ImGuiAPI::RenderBegin();

        for (auto& layer : mContext.layers)
        {
            layer->onCompute(deltaMilliseconds);
            if (!mContext.minimized)
            {
                layer->onRender(deltaMilliseconds);
            }
        }

        ImGuiAPI::RenderEnd();

        if (!mContext.minimized)
        {
            WindowAPI::SwapBuffers();
        }
    }
} // !namespace SE